CryptoとCrypto-jsの違い
SBJソリューション部のserinaです。
先日、ワンタイムパスワード生成のコードをPostmanで書く機会があったのですが、Crypto-jsしか対応していなかったためCryptoで書いたコードをCrypto-jsに書き直すという作業が発生しました。
せっかくなので、CryptoとCrypto-jsの違いを比較しようと思います。
前提知識
ワンタイムパスワード生成のコードは書いたことがなかったので、調べたときに知ったことをまとめます。
タイトルの内容とは少しズレるので、不要な方は読み飛ばしてください。
ワンタイムパスワードとは
ワンタイムパスワード(OTP)は、ユーザー認証のために一度だけ使用されるパスワードです。
ログインする際に、メールやメッセージに送られてきた6桁の番号を入力することがあると思います。
その番号がワンタイムパスワードになります。
ワンタイムパスワードの生成方法は、2つあります。
-
Time-Based One-Time Password Algorithm(TOTP)
タイムスタンプ認証。
RFC 6238に定義されている生成方法。
時刻に基づいてOTPを生成し、一定時間ごとに更新される。- 例: Google AuthenticatorやAuthyなどのアプリを使用して、30秒ごとに変わる数字を入力することで、ウェブサイトやサービスにログインする。
-
HMAC-Based One-Time Password Algorithm(HOTP)
カウンタ同期認証。
RFC 4226に定義されている生成方法。
カウンタに基づいてOTPを生成し、カウンタの同期が必要。- 例: 銀行のトークンデバイスが、ボタンを押すたびに新しいOTPを生成し、それをオンラインバンキングのログイン時に入力する。
SHAとは
SHAは、Secure Hash Algorithmの略です。
データのハッシュ値を生成するための暗号学的ハッシュ関数です。
SHA-1, SHA-2, SHA-3の3つあります。
- SHA-1 ... 脆弱性があるため非推奨
- SHA-2 ... 現時点で広く使われている
- SHA-3 ... SHA-2とは違うアルゴリズムを使用しており、元はKeccak(キーチャック)という名称だった
SHA-2の中でも、セキュリティやコスト計算の面でSHA-256が一番使用されているようです。
他には、SHA-224、SHA-384、SHA-512があります。
CryptoとCrypto-js
Crypto
ウェブアプリケーションで暗号化機能を実現するための標準APIで、ブラウザ環境で利用可能です。
Web Crypto APIと呼ばれています。
MDN Web Docsのページは、こちらになります。
Crypto-js
JavaScriptの暗号化用ライブラリです。
GitHubリポジトリは、こちらになります。
比較
特徴/用途 | Crypto (Web Crypto API) | Crypto-js |
---|---|---|
処理 | 非同期処理 | 同期処理 |
使いやすさ | ◯ | ◎ |
非同期処理の方が書き方が複雑になるので、Crypto-jsの方が簡単に書けると感じました。
両者の違いではまったところ
Crypto-jsは、 WordArray
という独自のデータ型を使用しており、Cryptoで書いた処理と合わせるには、WordArray
からUint8Array
に変換する処理を追加する必要がありました。
最初はこれに気づかず、生成されるワンタイムパスワードが同じにならずはまりました、、、。
CryptoJS.enc.Utf8.parseというメソッドがあるのですが、こちらが返す型がWordArray
になります。
そのため、このメソッドで返却された値を以下のコードを使用して、Uint8Array
に変換します。
WordArray
とUint8Array
を相互に変換するコード
const CryptoJS = require('crypto-js')
CryptoJS.enc.u8array = {
/**
* Converts a word array to a Uint8Array.
* @param {WordArray} wordArray The word array.
* @return {Uint8Array} The Uint8Array.
* @static
* @example var u8arr = CryptoJS.enc.u8array.stringify(wordArray);
*/
stringify: function (wordArray) {
var words = wordArray.words;
var sigBytes = wordArray.sigBytes;
var u8 = new Uint8Array(sigBytes);
for (var i = 0; i < sigBytes; i++) {
var bytes = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
u8[i] = bytes;
}
return u8;
},
/**
* Converts a Uint8Array to a word array.
* @param {string} u8Str The Uint8Array.
* @return {WordArray} The word array.
* @static
* @example var wordArray = CryptoJS.enc.u8array.parse(u8arr);
*/
parse: function (u8arr) {
var len = u8arr.length;
var words = [];
for (var i = 0; i < len; i++) {
words[i >>> 2] |= (u8arr[i] & 0xff) << (24 - (i % 4) * 8);
}
return CryptoJS.lib.WordArray.create(words, len);
}
}
最後に
似てるライブラリがあったときに両者の違いをしっかり調べることがあまりなかったので、この記事を書いたことで頭の中が整理されました。
そもそも暗号化まわりの知識がほとんどなかったので、この辺りの知識も身につけたいと思いました。
そして、はまった箇所については上司とペアワークで解決できたのですが、上司がほとんど解決してくれました!
自分でも時間をかけずに解決できるように、原因を予測するスキルを伸ばしていきたいです。